package org.jflame.context.filemanager;

import org.apache.commons.lang3.ArrayUtils;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.jflame.commons.file.FileHelper;
import org.jflame.commons.util.IOHelper;
import org.jflame.commons.util.StringHelper;
import org.jflame.context.filemanager.FastDFSFileManager.FastDFSCfg;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.*;
import java.util.Map.Entry;

/**
 * FastDFS文件管理. 注:一个实例一个连接,非线程安全
 *
 * @author charles.zhang
 */
public class FastDFSFileManager extends RemoteFileManager<FastDFSCfg> {

    public FastDFSFileManager(FastDFSCfg cfg) {
        super(cfg);
        try {
            // 从独立配置文件初始
            if (StringHelper.isNotEmpty(cfg.getCfgFile())) {
                String ext = FileHelper.getExtension(cfg.getCfgFile(), false);
                if ("properties".equals(ext)) {
                    ClientGlobal.initByProperties(cfg.getCfgFile());
                } else {
                    ClientGlobal.init(cfg.getCfgFile());
                }
            } else {
                ClientGlobal.initByProperties(cfg.toInitProperties());
            }
            logger.info("加载fastdfs信息:{}", ClientGlobal.configInfo());
        } catch (IOException | MyException e) {
            throw new IllegalStateException("fastdfs client加载失败", e);
        }
    }

    @Override
    public String save(File file, String filePath, Map<String, String> fileMeta) throws FileAccessException {
        try {
            return save(new FileInputStream(file), file.getName(), fileMeta);
        } catch (FileNotFoundException e) {
            throw new FileAccessException(e.getMessage());
        }
    }

    @Override
    public String save(InputStream fileStream, String fileName, Map<String, String> fileMeta, boolean replaceExisting)
            throws FileAccessException {
        try {
            byte[] fileBytes = IOHelper.readBytes(fileStream);
            return save(fileBytes, fileName, fileMeta);
        } catch (IOException e) {
            throw new FileAccessException("文件上传失败", e);
        } finally {
            IOHelper.closeQuietly(fileStream);
        }
    }

    @Override
    public String save(byte[] fileBytes, String fileName, Map<String, String> fileMeta) throws FileAccessException {
        NameValuePair[] metaList = setMetas(fileMeta);
        StorageClient1 storageClient = null;
        try {
            storageClient = initConnect();
            return storageClient.upload_file1(getConfig().groupName(), fileBytes,
                    FileHelper.getExtension(fileName, false), metaList);
        } catch (MyException | IOException e) {
            throw new FileAccessException("文件上传失败", e);
        } finally {
            closeClient(storageClient);
        }
    }

    @Override
    public byte[] readBytes(String filePath) throws FileAccessException {
        StorageClient1 storageClient = null;
        try {
            storageClient = initConnect();
            byte[] content = storageClient.download_file1(getFileId(filePath));
            return content;
        } catch (MyException | IOException e) {
            throw new FileAccessException("读取文件异常", e);
        } finally {
            closeClient(storageClient);
        }
    }

    @Override
    public File read(String filePath) throws FileAccessException {
        String key = getFileId(filePath);
        Path tmpPath = mappingTmpFile(key);
        // 使用缓存的临时文件
        Optional<File> localCacheFileOpl = getTmpCacheFile(tmpPath);
        if (localCacheFileOpl.isPresent()) {
            return localCacheFileOpl.get();
        }
        StorageClient1 storageClient = null;
        try {
            storageClient = initConnect();
            int i = storageClient.download_file1(key, tmpPath.toString());
            if (i == 0) {
                File tmpFile = tmpPath.toFile();
                tmpFile.deleteOnExit();
                return tmpFile;
            } else {
                throw new FileAccessException(key + "文件不存在", FileAccessException.FILE_NOT_FOUND);
            }
        } catch (MyException | IOException e) {
            throw new FileAccessException("读取文件异常", e);
        } finally {
            closeClient(storageClient);
        }
    }

    @Override
    public InputStream readStream(String filePath) throws FileAccessException {
        return new ByteArrayInputStream(readBytes(filePath));
    }

    @Override
    public void download(String filePath, Path savePath) throws FileAccessException {
        StorageClient1 storageClient = null;
        try {
            storageClient = initConnect();
            int i = storageClient.download_file1(getFileId(filePath), savePath.toString());
            if (i != 0) {
                throw new FileAccessException("文件下载失败" + filePath);
            }
        } catch (MyException | IOException e) {
            throw new FileAccessException("下载文件异常" + filePath, e);
        } finally {
            closeClient(storageClient);
        }

    }

    @Override
    public int delete(String... filePaths) throws FileAccessException {
        return delete(null, filePaths);
    }

    @Override
    public int delete(String groupName, String[] filePaths) throws FileAccessException {
        if (filePaths.length == 0) {
            return 0;
        }
        List<String> fileKeys = Arrays.asList(filePaths);
        if (logger.isInfoEnabled()) {
            logger.info("fastdfs删除文件:{}", ArrayUtils.toString(fileKeys));
        }
        for (int i = 0; i < filePaths.length; i++) {
            fileKeys.set(i, getFileId(fileKeys.get(i)));
        }
        int i = 0;
        StorageClient1 storageClient = null;
        try {
            storageClient = initConnect();
            for (String fileId : fileKeys) {
                i = i + storageClient.delete_file1(fileId);
            }
        } catch (MyException | IOException e) {
            throw new FileAccessException("删除文件异常", e);
        } finally {
            closeClient(storageClient);
        }
        return i;
    }

    @Override
    public String copy(String srcFilePath, String destFilePath) throws FileAccessException {
        StorageClient1 storageClient = null;
        String fileId = getFileId(srcFilePath);
        String extension = FileHelper.getExtension(srcFilePath, false);
        try {
            storageClient = initConnect();
            NameValuePair[] metaInfo = storageClient.get_metadata1(fileId);
            byte[] content = storageClient.download_file1(fileId);
            return storageClient.upload_file1(content, extension, metaInfo);
        } catch (MyException | IOException e) {
            logger.error("fastdfs复制文件异常,参数: 源={},目标={}", srcFilePath, destFilePath);
            throw new FileAccessException("复制文件异常", e);
        } finally {
            closeClient(storageClient);
        }
    }

    @Override
    public void close() {

    }

    void closeClient(StorageClient storageClient) {
        if (storageClient != null) {
            try {
                storageClient.close();
            } catch (Exception e) {
                e.printStackTrace();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    private StorageClient1 initConnect() throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getTrackerServer();
        StorageServer storageServer = null;
        return new StorageClient1(trackerServer, storageServer);
    }

    private NameValuePair[] setMetas(Map<String, String> fileMeta) {
        NameValuePair[] metaList = null;
        if (fileMeta != null) {
            int h = fileMeta.size();
            if (h > 0) {
                metaList = new NameValuePair[h];
                int i = 0;
                for (Entry<String, String> entry : fileMeta.entrySet()) {
                    metaList[i] = new NameValuePair(entry.getKey(), entry.getValue());
                    i++;
                }
            }
        }
        return metaList;
    }

    @Override
    public List<String> listFiles(String dir, int maxFiles, boolean isExcludeDir) throws FileAccessException {
        throw new UnsupportedOperationException("fastdfs不支持文件列举");
    }

    /**
     * FastDFS配置类. 配置属性:
     * <pre>
     * fastdfs.connect_timeout_in_seconds=10
     * fastdfs.network_timeout_in_seconds=20
     * fastdfs.charset=UTF-8
     * fastdfs.http_anti_steal_token = false
     * fastdfs.http_tracker_http_port=8880
     * #fastdfs.http_secret_key=FastDFS1234567891
     * fastdfs.tracker_servers=10.18.200.95:22122
     * </pre>
     */
    public static class FastDFSCfg extends FileManagerCfg {

        private String cfgFile;
        private int connectTimeoutInSeconds = 10;
        private int networkTimeoutInSeconds = 20;
        private String charset = StandardCharsets.UTF_8.name();
        private boolean httpAntiStealToken = false;
        private Integer httpTrackerHttpPort;
        private String trackerServers;

        public FastDFSCfg() {
        }

        public FastDFSCfg(String savePath, String server) {
            super(savePath, server);
        }

        public Properties toInitProperties() {
            Properties prop = new Properties();
            prop.put("fastdfs.connect_timeout_in_seconds", connectTimeoutInSeconds);
            prop.put("fastdfs.network_timeout_in_seconds", networkTimeoutInSeconds);
            prop.setProperty("fastdfs.charset", charset);
            prop.put("fastdfs.http_anti_steal_token", httpAntiStealToken);
            prop.put("fastdfs.http_tracker_http_port", httpTrackerHttpPort);
            prop.setProperty("fastdfs.tracker_servers", trackerServers);
            return prop;
        }

        public String groupName() {
            return super.getSavePath();
        }

        public String getCfgFile() {
            return cfgFile;
        }

        public void setCfgFile(String cfgFile) {
            this.cfgFile = cfgFile;
        }

        public int getConnectTimeoutInSeconds() {
            return connectTimeoutInSeconds;
        }

        public void setConnectTimeoutInSeconds(int connectTimeoutInSeconds) {
            this.connectTimeoutInSeconds = connectTimeoutInSeconds;
        }

        public int getNetworkTimeoutInSeconds() {
            return networkTimeoutInSeconds;
        }

        public void setNetworkTimeoutInSeconds(int networkTimeoutInSeconds) {
            this.networkTimeoutInSeconds = networkTimeoutInSeconds;
        }

        public String getCharset() {
            return charset;
        }

        public void setCharset(String charset) {
            this.charset = charset;
        }

        public boolean getHttpAntiStealToken() {
            return httpAntiStealToken;
        }

        public void setHttpAntiStealToken(boolean httpAntiStealToken) {
            this.httpAntiStealToken = httpAntiStealToken;
        }

        public Integer getHttpTrackerHttpPort() {
            return httpTrackerHttpPort;
        }

        public void setHttpTrackerHttpPort(Integer httpTrackerHttpPort) {
            this.httpTrackerHttpPort = httpTrackerHttpPort;
        }

        public String getTrackerServers() {
            return trackerServers;
        }

        public void setTrackerServers(String trackerServers) {
            this.trackerServers = trackerServers;
        }

    }
}
